#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QHash>
#include <QMap>
#include <QPair>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QStringList>
#include <QVariant>

#include <iostream>
#include <memory>
#include <memory>
#include <algorithm>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#define private public
#include "application/dispatcher/route_dispatcher.h"
#include "application/map/map_application_service.h"
#undef private

#include "application/vehicle/vehicle_operation_dispatcher.h"
#include "application/pathfinding/multi_agent_solver.h"
#include "application/pathfinding/temporal_graph.h"
#include "application/dispatcher/concurrent_combination_solver.h"
#include "infrastructure/configuration/iconfiguration_validator.h"
#include "infrastructure/factories/logger_factory.h"
#include "infrastructure/map/map_service.h"
#include "infrastructure/services/common_service.h"
#include "kernel/agv/i_agv_command.h"
#include "kernel/domain/map.h"
#include "kernel/interfaces/IConfiguration.h"
#include "kernel/interfaces/Result.h"

// 编码设置相关头文件
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#endif

#if defined(_MSC_VER) && (_MSC_VER >= 1600)
# pragma execution_character_set("utf-8")
#endif


using application::dispatcher::RouteDispatcher;
using application::pathfinding::AgentPlan;
using application::pathfinding::AgentRequest;

namespace {
    /**
     * @brief 配置跨平台文本编码格式
     * @details 设置中文编码，确保正确显示中文，支持不同Qt版本
     */
    static void configureTextEncoding() {

#if (QT_VERSION <= QT_VERSION_CHECK(5, 0, 0))
#if _MSC_VER
        QTextCodec* codec = QTextCodec::codecForName("utf-8"); // 统一使用UTF-8
#else
        QTextCodec* codec = QTextCodec::codecForName("utf-8");
#endif
        QTextCodec::setCodecForLocale(codec);
        QTextCodec::setCodecForCStrings(codec);
        QTextCodec::setCodecForTr(codec);
#elif (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
        QTextCodec* codec = QTextCodec::codecForName("utf-8");
        QTextCodec::setCodecForLocale(codec);
#else
        // Qt6不需要额外设置
#endif

#if defined(_WIN32) || defined(_WIN64)
        // 最早设置控制台代码页为UTF-8，确保所有输出正常显示
        SetConsoleOutputCP(65001);
        SetConsoleCP(65001);
#endif
    }

class RecordingAgvCommand : public kernel::agv::IAgvCommand {
public:
    struct MoveRecord {
        int agvNumber{0};
        std::vector<std::string> stations;
    };

    kernel::Result<void> moveToStationList(int agvNumber,
                                           const std::vector<std::string>& stations,const std::string& operation = "") override {
        MoveRecord record;
        record.agvNumber = agvNumber;
        record.stations = stations;
        m_records.push_back(std::move(record));
        return kernel::Result<void>::success();
    }

    kernel::Result<void> moveToStation(int, const std::string&, const std::string&) override {
        return kernel::Result<void>::success();
    }
    kernel::Result<void> cancelTask(int) override { return kernel::Result<void>::success(); }
    kernel::Result<void> pauseTask(int) override { return kernel::Result<void>::success(); }
    kernel::Result<void> resumeTask(int) override { return kernel::Result<void>::success(); }
    kernel::Result<void> controlJack(int, bool) override { return kernel::Result<void>::success(); }
    kernel::Result<void> startCharging(int, const std::string&) override {
        return kernel::Result<void>::success();
    }
    kernel::Result<void> stopCharging(int) override { return kernel::Result<void>::success(); }
    kernel::Result<void> controlDoor(int, int, bool) override { return kernel::Result<void>::success(); }
    kernel::Result<void> switchMap(int, const std::string&, const std::string&) override {
        return kernel::Result<void>::success();
    }
    kernel::Result<void> requestCurrentMap(int) override { return kernel::Result<void>::success(); }

    const std::vector<MoveRecord>& records() const { return m_records; }

private:
    std::vector<MoveRecord> m_records;
};

class StubConfiguration : public kernel::IConfiguration {
public:
    QString getString(const QString& key, const QString& defaultValue = "") const override {
        return m_values.value(key, defaultValue);
    }

    int getInt(const QString& key, int defaultValue = 0) const override {
        bool ok = false;
        const int value = m_values.value(key, QString::number(defaultValue)).toInt(&ok);
        return ok ? value : defaultValue;
    }

    bool getBool(const QString& key, bool defaultValue = false) const override {
        const auto str = m_values.value(key, defaultValue ? "true" : "false").toLower();
        if (str == "true" || str == "1") return true;
        if (str == "false" || str == "0") return false;
        return defaultValue;
    }

    double getDouble(const QString& key, double defaultValue = 0.0) const override {
        bool ok = false;
        const double value = m_values.value(key, QString::number(defaultValue)).toDouble(&ok);
        return ok ? value : defaultValue;
    }

    bool setString(const QString& key, const QString& value) override {
        m_values.insert(key, value);
        return true;
    }

    bool setInt(const QString& key, int value) override {
        m_values.insert(key, QString::number(value));
        return true;
    }

    bool setBool(const QString& key, bool value) override {
        m_values.insert(key, value ? "true" : "false");
        return true;
    }

    bool setDouble(const QString& key, double value) override {
        m_values.insert(key, QString::number(value));
        return true;
    }

    bool removeKey(const QString& key) override { return m_values.remove(key) > 0; }
    bool save() override { return true; }

    kernel::SubscriptionHandle subscribe(const QString& key,
                                         kernel::ConfigurationChangeCallback callback) override {
        const auto handle = m_nextHandle++;
        m_subscribers.insert(handle, {key, std::move(callback)});
        return handle;
    }

    void unsubscribe(kernel::SubscriptionHandle handle) override { m_subscribers.remove(handle); }

    bool isValid() const override { return true; }

    infrastructure::ValidationResult getValidationResult() const override {
        return infrastructure::ValidationResult(true);
    }

    std::optional<std::shared_ptr<kernel::IConfigurationSection>> getSection(
        const QString&) const override {
        return std::nullopt;
    }

    bool exportToFile(const QString&) const override { return true; }
    bool importFromFile(const QString&) override { return true; }
    QString exportToJson() const override { return "{}"; }
    bool importFromJson(const QString&) override { return true; }

private:
    QHash<QString, QString> m_values;
    kernel::SubscriptionHandle m_nextHandle{1};
    struct SubscriberEntry {
        QString key;
        kernel::ConfigurationChangeCallback callback;
    };
    QHash<kernel::SubscriptionHandle, SubscriberEntry> m_subscribers;
};

struct MapContext {
    std::shared_ptr<application::MapApplicationService> mapService;
    QString mapName;
    QString startStation;
    QString goalStation;
    int floor{0};
};

std::optional<MapContext> createMapContext() {
    auto loggerFactory = std::make_shared<infrastructure::factories::LoggerFactory>();
    auto configService = std::make_shared<StubConfiguration>();
    auto commonService =
        std::make_shared<infrastructure::services::CommonService>(loggerFactory, configService);
    auto infraMapService =
        std::make_shared<infrastructure::MapService>(loggerFactory->getDefaultLogger().get());
    auto mapService =
        std::make_shared<application::MapApplicationService>(infraMapService, commonService);

    QDir appDir(QCoreApplication::applicationDirPath());
    const QString configPath = appDir.filePath("config/app_config.json");
    QFile configFile(configPath);
    if (!configFile.open(QIODevice::ReadOnly)) {
        qWarning() << "Failed to open config file" << configPath;
        return std::nullopt;
    }

    const auto doc = QJsonDocument::fromJson(configFile.readAll());
    const auto root = doc.object();
    const auto mapSection = root.value("map").toObject();
    if (mapSection.isEmpty()) {
        qWarning() << "Config missing map section";
        return std::nullopt;
    }

    const QString itemName = mapSection.value("itemName").toString("demo-map");
    const int defaultFloor = mapSection.value("defaultFloor").toInt();
    const auto mapsArray = mapSection.value("maps").toArray();

    MapContext ctx;
    ctx.mapService = mapService;
    ctx.mapName = itemName;
    ctx.floor = defaultFloor;

    for (const auto& entry : mapsArray) {
        const auto obj = entry.toObject();
        const QString userName = obj.value("userName").toString();
        const int floor = obj.value("floor").toInt();
        const QString fileName = obj.value("filePath").toString();
        const QString mapPath = appDir.filePath(QStringLiteral("maps/%1").arg(fileName));

        auto loadResult = infraMapService->loadMapFromFile(mapPath);
        if (!loadResult.isSuccess()) {
            qWarning() << "Failed to load map file" << mapPath << ":" << loadResult.getErrorMessage();
            continue;
        }

        auto mapData = loadResult.getValue();

        application::FloorMapInfo info;
        info.floor = floor;
        info.userName = userName;
        info.filePath = mapPath;
        info.mapData = mapData;
        info.isLoaded = true;

        mapService->m_floorMaps.insert(floor, info);
        mapService->m_mapNameToFloor.insert(userName, floor);

        if (floor == defaultFloor) {
            ctx.mapName = userName;
            ctx.floor = floor;
            const auto& stations = mapData->getStationPoints();
            if (!stations.isEmpty()) {
                ctx.startStation = stations.first().instanceName;
                ctx.goalStation =
                    stations.size() > 1 ? stations[1].instanceName : stations.first().instanceName;
            }
        }
    }

    if (mapService->m_floorMaps.isEmpty()) {
        qWarning() << "No maps loaded from config";
        return std::nullopt;
    }

    mapService->m_currentFloor = ctx.floor;
    mapService->m_itemName = itemName;
    mapService->m_currentPosition = kernel::Position(0.0, 0.0);
    mapService->m_currentAngle = 0.0;

    if (ctx.startStation.isEmpty()) {
        qWarning() << "Unable to determine start station";
        return std::nullopt;
    }

    return ctx;
}

struct TaskDefinition {
    int agvNumber;
    QString start;
    QString goal;
};

void logAgentPlans(const std::shared_ptr<application::MapApplicationService>& mapService,
                   const std::vector<TaskDefinition>& tasks) {
    auto graph = mapService->getPathfindingGraph(mapService->getCurrentFloor());
    if (!graph || graph->isEmpty()) {
        qWarning() << "[Sandbox] Pathfinding graph is empty, skip plan log";
        return;
    }

    std::vector<AgentRequest> requests;
    requests.reserve(tasks.size());
    for (const auto& task : tasks) {
        requests.push_back({task.agvNumber, task.start, task.goal});
    }

    std::vector<AgentPlan> plans;
    {
        application::dispatcher::ConcurrentCombinationSolver concurrentSolver;
        auto candidates =
            application::dispatcher::ConcurrentCombinationSolver::generateCandidates(requests);
        qDebug() << "[Sandbox] generated" << candidates.size() << "candidates";
        for (int idx = 0; idx < static_cast<int>(candidates.size()); ++idx) {
            const auto& c = candidates[idx];
            QStringList reqLabels;
            for (const auto& r : c.requests) {
                reqLabels.append(
                    QString("AGV%1:%2->%3").arg(r.agvNumber).arg(r.startId).arg(r.goalId));
            }
            qDebug() << "  candidate" << idx << "priority" << c.priority << ":" << reqLabels.join(", ");
        }
        auto result = concurrentSolver.solveWithFallback(
            candidates, graph, nullptr, 10 /*timeout seconds*/);
        if (!result.success) {
            qWarning() << "[Sandbox] Concurrent solver failed, cannot produce visualized output"
                       << result.errorMessage;
            return;
        }
        plans = result.plans;
    }

    qDebug() << "=== Multi-Agent Plan Summary ===";
    for (const auto& plan : plans) {
        QStringList route;
        route.reserve(plan.route.size());
        for (const auto& node : plan.route) {
            route.append(node);
        }
        qDebug() << "AGV" << plan.agvNumber << ":" << route.join(" -> ");
    }

    QMap<QString, QVector<QPair<int, int>>> stationArrivals;
    for (const auto& plan : plans) {
        for (int idx = 0; idx < plan.route.size(); ++idx) {
            const auto& station = plan.route[idx];
            int time = (idx < plan.times.size()) ? plan.times[idx] : idx;
            stationArrivals[station].append(qMakePair(plan.agvNumber, time));
        }
    }

    qDebug() << "=== Station Arrival Order ===";
    for (auto it = stationArrivals.constBegin(); it != stationArrivals.constEnd(); ++it) {
        auto arrivals = it.value();
        std::sort(arrivals.begin(), arrivals.end(),
                  [](const QPair<int, int>& lhs, const QPair<int, int>& rhs) {
                      if (lhs.second != rhs.second) return lhs.second < rhs.second;
                      return lhs.first < rhs.first;
                  });

        QStringList order;
        for (const auto& pair : arrivals) {
            order.append(QString("AGV%1(t=%2)").arg(pair.first).arg(pair.second));
        }
        qDebug() << it.key() << ":" << order.join(" -> ");

        if (arrivals.size() >= 2) {
            qDebug() << "  " << it.key() << ":"
                     << QString("AGV%1 first, AGV%2 second")
                            .arg(arrivals.front().first)
                            .arg(arrivals[1].first);
        }
    }

    application::pathfinding::TemporalGraph temporalGraph;
    const auto temporalResult = temporalGraph.build(plans);

    qDebug() << "=== Temporal Graph Segments ===";
    for (auto it = temporalResult.segmentsByAgv.constBegin();
         it != temporalResult.segmentsByAgv.constEnd(); ++it) {
        const int agvId = it.key();
        const auto& segments = it.value();
        qDebug() << "AGV" << agvId << ":" << segments.size() << "segments";
        for (const auto& seg : segments) {
            qDebug() << "   -" << seg.join(" -> ");
        }
    }

    qDebug() << "=== Temporal Graph Wait Constraints ===";
    if (temporalResult.waitConstraints.isEmpty()) {
        qDebug() << "  (none)";
    } else {
        for (const auto& wait : temporalResult.waitConstraints) {
            qDebug() << QString("  AGV%1 waits for AGV%2 to clear %3 -> %4 at index %5")
                            .arg(wait.waiterAgv)
                            .arg(wait.awaitedAgv)
                            .arg(wait.unlockFrom)
                            .arg(wait.unlockTo)
                            .arg(wait.waiterStationIndex);
        }
    }

    qDebug() << "=== Temporal Graph Special Stations ===";
    if (temporalResult.specialStationWhiteList.isEmpty()) {
        qDebug() << "  (none)";
    } else {
        QStringList specials;
        specials.reserve(temporalResult.specialStationWhiteList.size());
        for (auto it = temporalResult.specialStationWhiteList.constBegin();
             it != temporalResult.specialStationWhiteList.constEnd(); ++it) {
            specials.append(it.key());
        }
        qDebug() << "  " << specials.join(", ");
    }

    const auto eventTypeToString =
        [](application::pathfinding::TemporalEventType type) -> QString {
        switch (type) {
        case application::pathfinding::TemporalEventType::LocationEntry:
            return QStringLiteral("Entry");
        case application::pathfinding::TemporalEventType::SafetyDepart:
            return QStringLiteral("SafetyDepart");
        case application::pathfinding::TemporalEventType::SafetyArrive:
            return QStringLiteral("SafetyArrive");
        }
        return QStringLiteral("Unknown");
    };

    qDebug() << "=== Temporal Graph Schedule ===";
    for (const auto& event : temporalResult.events) {
        const double time =
            temporalResult.schedule.eventTimes.value(event.id, 0.0);
        const QString line =
            QStringLiteral("  [E%1] AGV%2 %3 %4 idx=%5 t=%6")
                .arg(event.id)
                .arg(event.agvNumber)
                .arg(eventTypeToString(event.type))
                .arg(event.stationId)
                .arg(event.routeIndex)
                .arg(QString::number(time, 'f', 2));
        qDebug().noquote() << line;
    }
}

}  // namespace

int main(int argc, char** argv) {
    configureTextEncoding();
    QCoreApplication app(argc, argv);

    auto mapContextOpt = createMapContext();
    if (!mapContextOpt) {
        qCritical() << "Failed to initialize map context";
        return 1;
    }
    auto& mapContext = *mapContextOpt;

    auto dispatcherBackend =
        std::make_shared<application::vehicle::VehicleOperationDispatcher>();
    auto vehicleService =
        std::make_shared<application::vehicle::VehicleService>(nullptr, dispatcherBackend);

    auto command = std::make_shared<RecordingAgvCommand>();
    auto registerResult = vehicleService->registerVehicle(1, command);
    if (!registerResult.isSuccess()) {
        qCritical() << "Failed to register AGV:" << registerResult.getErrorMessage();
        return 1;
    }

    const QString mapName = QStringLiteral("XP1F");
    const int floor =
        mapContext.mapService->m_mapNameToFloor.value(mapName, mapContext.floor);
    mapContext.mapService->m_currentFloor = floor;
    mapContext.mapService->m_itemName = mapName;

    // const std::vector<TaskDefinition> tasks = {
    //     {9, QStringLiteral("LM224"), QStringLiteral("LM225")},
    //     {8, QStringLiteral("LM268"), QStringLiteral("AP211")},
    //     {12, QStringLiteral("LM11"), QStringLiteral("LM225")},   // 占位静止
    //     {7, QStringLiteral("LM291"), QStringLiteral("AP117")},
    //     {10, QStringLiteral("LM6"), QStringLiteral("LM6")},     // 占位静止
    // };
    const std::vector<TaskDefinition> tasks = {
        {9, QStringLiteral("LM145"), QStringLiteral("LM145")},
        {8, QStringLiteral("LM30"), QStringLiteral("AP33")},
    };

    for (const auto& task : tasks) {
        if (!vehicleService->getVehicle(task.agvNumber)) {
            auto reg = vehicleService->registerVehicle(task.agvNumber, command);
            if (!reg.isSuccess()) {
                qCritical() << "Failed to register AGV" << task.agvNumber << ":"
                            << reg.getErrorMessage();
                return 1;
            }
        }
        auto* agv = vehicleService->getVehicle(task.agvNumber);
        if (!agv) continue;
        agv->setCurrentMap(mapName.toStdString());
        agv->setCurrentStation(task.start.toStdString());
        agv->setOnlineStatus(true);
        agv->setControlledByDispatcher(false);
        agv->setBusyStatus(false);
        agv->setChargingStatus(false);
        agv->setLockedStatus(false);
        agv->setBatteryLevel(90);
        agv->setNavStatus(kernel::agv::NavStatus::None);
        agv->setTaskType(kernel::agv::TaskType::None);
        vehicleService->setVehicleNavTask(task.agvNumber, task.goal.toStdString());
    }

    RouteDispatcher dispatcher(mapName, floor, vehicleService, mapContext.mapService,
                               nullptr);
    dispatcher.init();

    for (const auto& task : tasks) {
        dispatcher.stationTaskFromServer(task.agvNumber, task.goal);
    }

    const auto updateAgvPosition = [&](int agvNumber) {
        auto* agv = vehicleService->getVehicle(agvNumber);
        if (!agv) return;
        const auto& history = command->records();
        for (auto it = history.rbegin(); it != history.rend(); ++it) {
            if (it->agvNumber == agvNumber && !it->stations.empty()) {
                agv->setCurrentStation(it->stations.back());
                break;
            }
        }
    };

    const auto hasPendingSegments = [&]() -> bool {
        for (auto it = dispatcher.m_plannedSegments.cbegin();
             it != dispatcher.m_plannedSegments.cend(); ++it) {
            if (!it.value().isEmpty()) return true;
        }
        return false;
    };

    const auto processCycle = [&]() {
        dispatcher.solveAndPublishNow();
        for (const auto& task : tasks) {
            updateAgvPosition(task.agvNumber);
            if (auto* agv = vehicleService->getVehicle(task.agvNumber)) {
                agv->setBusyStatus(false);
            }
        }
    };

    processCycle();
    while (hasPendingSegments()) {
        processCycle();
    }

    const auto& history = command->records();
    qDebug() << "Recorded segments (" << history.size() << "):";
    for (const auto& entry : history) {
        QStringList stations;
        stations.reserve(static_cast<int>(entry.stations.size()));
        for (const auto& s : entry.stations) {
            stations.append(QString::fromStdString(s));
        }
        qDebug() << "  AGV" << entry.agvNumber << ":" << stations.join(" -> ");
    }

    return 0;
}
